#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <omp.h>
#include <mpi.h>
#include "util.h"

#define NUM_BYTES 1336054
#define TAM_FRASE 300
#define TAM_PALAVRA 30

int main(int argc, char **argv)
{
	double tempo, inicial = omp_get_wtime();
	double tempoPal = 0, tempoPalIni = 0, tempoFrase = 0, tempoFraseIni = 0;
	int chamadaPalim = 0, chamadaFrase = 0; 

	int seek, numPalavras = 0, numFrases = 0, rank;
	
    	seek = NUM_BYTES;
    
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);

	if(rank == 0) {
		char *texto, *aux;
		char c;
		int i = 0, j;

		/* Usado para guardar um pedaço de palavra que foi cortado durante a decomposição */
		aux = (char *)malloc(TAM_FRASE*sizeof(char));
		/* Armazena blocos de textos para as threads */
		texto = (char*)malloc((seek + TAM_FRASE + 1)*sizeof(char));

	        FILE *arq = fopen("shakespe.txt", "r");
	
		/*
			Faz a leitura de blocos de texto para distribuição entre as threads
		*/
        	fread(texto, sizeof(char), seek, arq);
	
		/*
			Verifica se cortou uma palavra no meio durante a decomposição, se sim, anda no
			arquivo até achar o fim da frase.
		*/
		c = fgetc(arq);
		while(c != '\n' && c != '!' && c != '?' && c != ';' && c != ':' && c != '\r' )
		{
			aux[i] = c;
			c = fgetc(arq);
			i++;		
		}

		/* Concatena o pedaço cortado da palavra com o bloco anterior */
		aux[i] = '\0';
		strcat(texto, aux);
	
		/* Manda o offset para a próxima thread */
		i += seek;
		MPI_Send(&i, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
		
		/*
			Chama a função separaPalavra para o bloco texto[rank], dentro dessa função as
			palavras são parseadas e verificadas quanto a palíndromos.
		*/
		tempoPalIni = omp_get_wtime();
        		numPalavras += separaPalavra(texto, &chamadaPalim);
		tempoPal = tempoPal + (omp_get_wtime() - tempoPalIni);

		/*
			Chama a função separaFrase para o bloco texto[rank], dentra dessa função as
			frases são parseadas e verificadas quanto a palíndromos.
		*/
		tempoFraseIni = omp_get_wtime();
        		numFrases += separaFrase(texto,&chamadaFrase);
		tempoFrase = tempoFrase + (omp_get_wtime() - tempoFraseIni);
	
		/*
			Espera o resultado dos outros processos
		*/		
		MPI_Recv(&i, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(&j, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

		numPalavras += i;
		numFrases += j;

		MPI_Recv(&i, 1, MPI_INT, 2, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(&j, 1, MPI_INT, 2, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

		numPalavras += i;
		numFrases += j;

		MPI_Recv(&i, 1, MPI_INT, 3, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(&j, 1, MPI_INT, 3, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

		numPalavras += i;
		numFrases += j;
	
        	fclose(arq);

		tempo = omp_get_wtime() - inicial;

		printf("Processo %d - Palindromos: [%d , %.2lf] - Frase: [%d , %.2lf]\n",rank,chamadaPalim, tempoPal,chamadaFrase, tempoFrase);
	 
		printf("\n\nQuantidade de palindromos de palavras: %d\n", numPalavras);
   		printf("Quantidade de palindromos de frase: %d\n", numFrases);
		printf("Tempo de execução: %.2lf seg\n", tempo);

	} else if(rank == 1 || rank == 2) {
	        char *texto, *aux;
		char c;
		int i = 0;
		
		/* Usado para guardar um pedaço de palavra que foi cortado durante a decomposição */
		aux = (char *)malloc(TAM_FRASE*sizeof(char));
		/* Armazena blocos de textos para as threads */
		texto = (char*)malloc((seek + TAM_FRASE + 1)*sizeof(char));

        	FILE *arq = fopen("shakespe.txt", "r");
	
		/* Recebe o offset do arquito do processo 0 */
		MPI_Recv(&i, 1, MPI_INT, rank-1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
	
		seek = i;
		i = 0;

		/* Posiciona o ponteiro do arquivo */
        	fseek(arq, seek*rank, SEEK_SET);
        	fread(texto, sizeof(char), seek, arq);

		/*
			Verifica se cortou uma palavra no meio durante a decomposição, se sim, anda no
			arquivo até achar o fim da frase.
		*/
		c = fgetc(arq);
		while(c != '\n' && c != '!' && c != '?' && c != ';' && c != ':' && c != '\r' ) {
			aux[i] = c;
			c = fgetc(arq);
			i++;	
		}

		/* Concatena o pedaço cortado da palavra com o bloco anterior */
		aux[i] = '\0';
		strcat(texto, aux);

		/* Manda o offset para o próximo processo */	
		i += seek;
		MPI_Send(&i, 1, MPI_INT, rank+1, 0, MPI_COMM_WORLD);

		/*
			Chama a função separaPalavra para o bloco texto[rank], dentro dessa função as
			palavras são parseadas e verificadas quanto a palíndromos.
		*/
		tempoPalIni = omp_get_wtime();
        		numPalavras += separaPalavra(texto,&chamadaPalim);
		tempoPal = tempoPal + (omp_get_wtime() - tempoPalIni);

		/* Manda o resultado para o processo mestre */	
		MPI_Send(&numPalavras, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
        
		/*
			Chama a função separaFrase para o bloco texto[rank], dentra dessa função as
			frases são parseadas e verificadas quanto a palíndromos.
		*/
		tempoFraseIni = omp_get_wtime();
		        numFrases += separaFrase(texto,&chamadaFrase);
		tempoFrase = tempoFrase + (omp_get_wtime() - tempoFraseIni);
	
		/* Manda o resultado para o processo mestre */
		MPI_Send(&numFrases, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
        
		fclose(arq);

		printf("Processo %d - Palindromos: [%d , %.2lf] - Frase: [%d , %.2lf]\n",rank,chamadaPalim, tempoPal,chamadaFrase, tempoFrase);
	} else if(rank == 3) {
	        char *texto, *aux;
		char c;
		int i = 0;

		/* Usado para guardar um pedaço de palavra que foi cortado durante a decomposição */
		aux = (char *)malloc(TAM_FRASE*sizeof(char));
		/* Armazena blocos de textos para as threads */
		texto = (char*)malloc((seek + TAM_FRASE + 1)*sizeof(char));

        	FILE *arq = fopen("shakespe.txt", "r");

		/* Recebe o offset do arquito do processo 2 */
		MPI_Recv(&i, 1, MPI_INT, 2, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
	
		seek = i;
		i = 0;

		/* Posiciona o ponteiro do arquivo */
        	fseek(arq, seek*rank, SEEK_SET);
        	fread(texto, sizeof(char), seek, arq);

		/*
			Chama a função separaPalavra para o bloco texto[rank], dentro dessa função as
			palavras são parseadas e verificadas quanto a palíndromos.
		*/        
		tempoPalIni = omp_get_wtime();
        		numPalavras += separaPalavra(texto,&chamadaPalim);
		tempoPal = tempoPal + (omp_get_wtime() - tempoPalIni);

		/* Manda o resultado para o processo mestre */	
		MPI_Send(&numPalavras, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
        
		/*
			Chama a função separaFrase para o bloco texto[rank], dentra dessa função as
			frases são parseadas e verificadas quanto a palíndromos.
		*/
		tempoFraseIni = omp_get_wtime();
		        numFrases += separaFrase(texto,&chamadaFrase);
		tempoFrase = tempoFrase + (omp_get_wtime() - tempoFraseIni);
	
		/* Manda o resultado para o proceso mestre */
		MPI_Send(&numFrases, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);

	        fclose(arq);

		printf("Processo %d - Palindromos: [%d , %.2lf] - Frase: [%d , %.2lf]\n",rank,chamadaPalim, tempoPal,chamadaFrase, tempoFrase);
	}
   
	MPI_Finalize();

    return 0;
}

